import pygame
import math
from pygame.locals import *
import sys
import random
import cairo
import numpy
import Image
import rsvg

pygame.init()
WIDTH = 640
HEIGHT = 480
screen = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)

def bgra_surf_to_rgba_string(cairo_surface):
    # We use PIL to do this
    img = Image.frombuffer(
        'RGBA', (cairo_surface.get_width(),
                 cairo_surface.get_height()),
        cairo_surface.get_data(), 'raw', 'BGRA', 0, 1)
 
    return img.tostring('raw', 'RGBA', 0, 1)

class UserInterface():
    def __init__(self):
        pass

class Object():
    def inheritStuff(self):
        self.canjump = False
        self.falling = False
        self.jump = 0 # height of current jump
        self.surfArray = self._generateSurfArray()

    def _generateSurfArray(self):
        surface = pygame.Surface((self.size*2, self.size*2))
        pygame.draw.circle(surface, (1,2,3), (self.size, self.size), self.size)
        return pygame.surfarray.array2d(surface)

class Hero(Object):
    def __init__(self):
        self.x = 200
        self.y = 50
        self.size = 5 #radius of cirlce
        self.maxheight = 40 # of jump
        self.jumpheight = 0
        self.direction = [1, 0] # which way you're facing. [x, y]. Can be -1,0, or 1.
        self.health = 10
        self._gunTimer = 0 # used in shooting() to limit shots per second
        self.inheritStuff()

    def update(self, world, clock):
        self.pressed_keys = pygame.key.get_pressed()
        self.movement()
        # self.display(world)
        self.shooting(world, clock)

    def movement(self):
        if self.pressed_keys[K_a]: # move left
            self.x -= 2
            self.direction[0] = -1
        if self.pressed_keys[K_d]: # move right
            self.x += 2
            self.direction[0] = +1
        if self.pressed_keys[K_SPACE] and self.canjump: # jump!
            if self.jump <= self.maxheight:
                self.y -= 3
                self.jump += 3
            else:
                self.canjump = False
                self.jump = 0

    def shooting(self, world, clock):
            if self._gunTimer > 0:
                self._gunTimer -= 1
            if self.pressed_keys[K_k] and self._gunTimer == 0:
                world.spawnBullet(self.direction, (self.x, self.y))
                self._gunTimer = 5

    def display(self, world):
        pygame.draw.circle(screen, (255,0,0), (
        self.x+world.cameraPosition[0],self.y+world.cameraPosition[1]), self.size)

class Bullet():
    def __init__(self, direction, position, world):
        self.x, self.y = position
        self.direction = [direction[0],direction[1]] # tuple or list
        self.speed = 7

    def update(self):
        self.travel()
        self.render()

    def render(self):
        pygame.draw.circle(screen, (0, 255, 255), (self.x+world.cameraPosition[0], self.y), 2)

    def travel(self):
        self.x += self.direction[0]*self.speed
        self.y += self.direction[1]*self.speed

class Enemy(Object):
    def __init__(self, position, hp, turret=False):
        self.x, self.y = position
        self.hp = hp
        self.size = 5
        self.red = 0
        self.green = 0
        self.blue = 0
        self.turret = turret
        self.direction = [1, 0]
        self.falling = True
        self.directionShotFrom = None
        self.bleeding = False
        self._gunTimer = 0
        self._bleedTimer = 10
        self.inheritStuff()

    def update(self, hero, world):
        self.ai(hero)
        self.render()
        self.red = 255 - self.hp*20
        self.green = self.hp*20
        if self.bleeding == True:
            self.bleed(world)

    def render(self):
        pygame.draw.circle(screen, (self.red, self.green, self.blue),
        (self.x+world.cameraPosition[0],self.y), self.size)

    def shoot(self):
        if self._gunTimer > 0:
            self._gunTimer -= 1
        if self._gunTimer == 0:
            world.spawnBullet(self.direction, 
                (self.x+(self.direction[0]*self.size+1), self.y+(self.direction[1]*self.size+1)))
            self._gunTimer = 5

    def ai(self, hero):
        if self.turret == True:
            if hero.y+hero.size > self.y-self.size and hero.y-hero.size < self.y+self.size:
                if hero.x < self.x:
                    self.direction = [-1, 0]
                else:
                    self.direction = [+1, 0]
                self.shoot()
        else:    
            if hero.x - self.x < 0:
                self.x -= 1
            else: self.x += 1

    def bleed(self, world):
        pygame.draw.line(screen, (255,0,0), (self.x+world.cameraPosition[0], 
        self.y+world.cameraPosition[1]), (self.x+world.cameraPosition[0]+
        self.directionShotFrom[0]*15, self.y+world.cameraPosition[1]))
        self._bleedTimer -= 1
        if self._bleedTimer == 0:
            self.bleeding = False
            self._bleedTimer = 10

class Item(Object): # anything that can be picked up -- powerups, guns, ammo, health, armor, etc
    def __init__(self, position):
        self.x, self.y = position
        self.inheritStuff()

class World():
    def __init__(self):
        svg_graphics = rsvg.Handle('/home/critter/brain-bacon/platformer/levels/test.svg')
        svg_size = svg_graphics.get_dimension_data()
        data = numpy.empty(svg_size[0] * svg_size[1] * 4, dtype=numpy.int8)
        cairo_surface = cairo.ImageSurface.create_for_data(
            data, cairo.FORMAT_ARGB32, svg_size[0], svg_size[1], svg_size[0] * 4)
        ctx = cairo.Context(cairo_surface)
        svg_graphics.render_cairo(ctx)
        data_string = bgra_surf_to_rgba_string(cairo_surface)
        self.levelGraphic = pygame.image.frombuffer(
          data_string, (svg_size[0], svg_size[1]), 'RGBA')
        self.levelSurface = pygame.surfarray.array2d(self.levelGraphic)
        # above: level loading
        self.enemies = [
        # Enemy((280, 180), 5, True) 
        ]
        self.gameObjects = [
        Hero()
        ]
        self.characters = list(self.enemies)
        # self.character.insert(0, 
        self.cameraPosition = [0,0]
        self.bullets = []
        self._gunTimer = 0
        self._next = 0
    def update(self, hero):
        self.drawPlatforms()
        self.camera(hero)
        self.handleBullets(hero)
        self.drawEnemies(hero)
        self.gravity(hero, self.enemies)

        # if self._gunTimer == 0:
        #     self._next = random.randint(20, 90)
        #     self.enemies.append(Enemy((random.randint(-100, 700), 40), random.randint(1, 10)))
        # self._gunTimer += 1
        # if self._gunTimer > self._next: self._gunTimer = 0

    def drawPlatforms(self):
        screen.blit(self.levelGraphic, (self.cameraPosition[0], self.cameraPosition[1]))

    def camera(self, hero):
        pressed_keys = pygame.key.get_pressed()
        if pressed_keys[K_LEFT]:
            self.cameraPosition[0] += 2
        if pressed_keys[K_RIGHT]:
            self.cameraPosition[0] -= 2
        if hero.x + self.cameraPosition[0] > WIDTH/2+50:
            self.cameraPosition[0] = -hero.x + WIDTH/2+50
        if hero.x + self.cameraPosition[0] < WIDTH/2-50:
            self.cameraPosition[0] = -hero.x + WIDTH/2-50

    def spawnBullet(self, direction, position):
        self.bullets.append(Bullet(direction, position, world))

    def handleBullets(self, hero):
        for b in self.bullets:
            b.update()
            if (b.x + self.cameraPosition[0] > hero.x +
            (WIDTH-(hero.x+world.cameraPosition[0]))+world.cameraPosition[0]
            or b.x + self.cameraPosition[0] < hero.x +
            (00-(hero.x+world.cameraPosition[0]))+world.cameraPosition[0]):
                self.bullets.remove(b)
            for e in self.enemies:
                 if Rect((b.x, b.y), (4, 4)).colliderect(Rect((e.x, e.y), (10, 10))):
                    e.hp -= 1
                    e.bleeding = True
                    e.directionShotFrom = b.direction
                    self.bullets.remove(b)

    def drawEnemies(self, hero):
        for e in self.enemies:
            e.update(hero, world)
            if e.hp <= 0:
                self.enemies.remove(e)

    def gravity(self, hero, enemies): # (and collision!)
        #if hero.pressed_keys[K_SPACE] == False or hero.canjump == False:
        for thing in self.gameObjects + self.enemies:
            thing.falling = True
            thing.y += 3 # make dude fall
            while self._colliding(thing) == True:
                thing.y -= 1
            if thing.falling:
                thing.canjump = False
                thing.jump = 0
            else: thing.canjump = True

    def _colliding(self, thing):
        square = self.levelSurface[thing.x-thing.size:
                           thing.x+thing.size,
                           thing.y-thing.size:
                           thing.y+thing.size]
        collision = False
        for _x in range(0, 10):
            for _y in range(0, 10):
                if square[_x, _y] != 0 and thing.surfArray[_x, _y] != 0:
                    collision = True
                    break
            if collision == True:
                break
        print collision
        return collision
        # below: enemies
        # for e in self.enemies:
        #     e.y += 3
        #     x = Rect(e.x-e.size, e.y-e.size, 10, 10).collidelist(self.platformRects)
        #     if x != -1 and e.y-3 < self.platformRects[x].top: #if there's a collision
        #         e.y = self.platformRects[x].top-e.size

world = World()
clock = pygame.time.Clock()

while True:
    clock.tick(30)
    screen.fill((255,255,255))
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    world.gameObjects[0].update(world, clock) # this must be called before...
    world.update(world.gameObjects[0]) # ...this, becuase it needs a varible passed by the above,
    world.gameObjects[0].display(world) # but this has to come after world.update so the visable sprite
                        # doesn't get ahead of the collision code and look glitchy
    
    # start = (300, 300)
    # end = pygame.mouse.get_pos()
    # pygame.draw.line(screen, (0,0,0), start, end)
    # angle = math.degrees(math.atan2(start[0] - end[0], start[1] - end[1]))#*180/math.pi*-1
    # if angle > 0:
    #     angle2 = angle - 90
    # else:                                     # ALL THIS CRAP IS FOR ROTATING RECTANGLES
    #     angle2 = angle + 90
    # angle2 = math.radians(angle2)
    # pygame.draw.line(screen, (0,0,0), start, 
    #     (start[0]+math.sin(angle2)*20, start[1]+math.cos(angle2)*20) )
    # pygame.draw.line(screen, (0,0,0), end, 
    #     (end[0]+math.sin(angle2)*20, end[1]+math.cos(angle2)*20) )
    # pygame.draw.line(screen, (0,0,0), (start[0]+math.sin(angle2)*20, start[1]+math.cos(angle2)*20), 
    #     (end[0]+math.sin(angle2)*20, end[1]+math.cos(angle2)*20) )
    pygame.display.update()